/**
 * 
 */
package com.bieber.smartpost.httpcore;

import java.io.*;
import java.net.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.bieber.smartpost.model.RequestEntity;
import com.bieber.smartpost.util.Utils;
import com.jbeer.framework.utils.StringUtils;

import javax.net.ssl.*;

/**
 * @author bieber 
 * 请求实体
 */
public class Request {

	private Header header;

	private HttpURLConnection connection;

	private OutputStream out;

	private String urlStr;
	
	private String requestData;
	
	private InputStream requestInputStream;

	private static final String PROTOCOL_PREFIX = "http";

    private ContentType contentType;
    
    private CookieManager manager;


    public Request(String urlString,String parameterType) throws IOException {
        if (StringUtils.isEmpty(urlString)) {
			throw new IllegalArgumentException("Illegal url:" + urlString);
		}
		if (urlString.indexOf(PROTOCOL_PREFIX) < 0) {
			throw new IllegalArgumentException("Illegal url:" + urlString);
		}
        contentType=ContentType.getType(parameterType);
		this.urlStr = urlString;
	}

	/**
	 * 設置請求頭部信息
	 * @param key
	 * @param value
	 */
	public void setHeader(String key, String value) {
		if (header == null) {
			header = new Header();
		}
		header.put(key, value);
	}

	/**
	 * 執行請求，通過傳輸請求的類型（GET/POST）
	 * @param type
	 * @return
	 * @throws Exception
	 */
	private Response execute(String type) throws Exception {
        manager = new CookieManager();
        manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        CookieHandler.setDefault(manager);
		URL url = null;
		boolean isGet = false;
		if (type.equalsIgnoreCase("GET")) {
            if(!StringUtils.isEmpty(requestData)){
                int paramIndex = urlStr.indexOf("?");
                if(paramIndex>=0){
                    if(paramIndex<urlStr.length()) {
                        urlStr +="&"+requestData;
                    }else{
                        urlStr +=requestData;
                    }
                }else{
                    urlStr+="?"+requestData;
                }
            }
			url = new URL(urlStr);
			isGet = true;
		} else {
			url = new URL(urlStr);
		}
        Utils.COMMONS_LOGGER.debug("start request,type: "+type+",url:"+urlStr);
        if(urlStr.startsWith("https")){
            Utils.COMMONS_LOGGER.debug("current request is https");
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() },
                    new java.security.SecureRandom());
            HttpsURLConnection tempConnection = (HttpsURLConnection) url.openConnection();
            tempConnection.setSSLSocketFactory(sc.getSocketFactory());
            tempConnection.setHostnameVerifier(new TrustAnyHostnameVerifier());
            connection = tempConnection;
            fillHeader();
            connection.setDoInput(true);
            connection.setRequestMethod(type);
            //connection.connect();
            Utils.COMMONS_LOGGER.debug("request had connected remote server");
        }else{
          //  Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress("127.0.0.1",8081));
		    connection = (HttpURLConnection) url.openConnection();
            fillHeader();
            connection.setDoInput(true);
            connection.setRequestMethod(type);
            //connection.connect();
            Utils.COMMONS_LOGGER.debug("request had connected remote server");
        }
		if (!isGet) {
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);

			out = connection.getOutputStream();
			if(!StringUtils.isEmpty(requestData)){
				this.requestInputStream=new ByteArrayInputStream(requestData.getBytes());
			}
			writeRequestData();
            Utils.COMMONS_LOGGER.debug("start write post data to remote server");
		}
		return generateResponse();
	}

	public Response post(String data) throws Exception {
		this.requestData=data.replaceAll("\\\\","");
		return execute("POST");
	}

    public Response post() throws Exception {
        return execute("POST");
    }

	public Response post(Map<String, String> params) throws Exception {
		this.requestData=parseParamsMap(params);
		return execute("POST");
	}

	public Response post(InputStream is) throws Exception {
		this.requestInputStream=is;
		return execute("POST");
	}

	public Response post(byte[] bytes) throws Exception {//http://www.oschina.net
		this.requestInputStream=new ByteArrayInputStream(bytes);
		return execute("POST");
	}
	
	public Response get(String data) throws Exception {
		this.requestData=data;
		return execute("GET");
	}

	public Response get(Map<String, String> params) throws Exception {
		this.requestData=parseParamsMap(params);
		return execute("GET");
	}

	/**
	 * 將POST的數據提交到請求服務器
	 * @throws IOException
	 */
	private void writeRequestData() throws IOException {
		if(requestInputStream!=null){
			byte[] buffer = new byte[1024];
			int offset=-1;
			while((offset=requestInputStream.read(buffer, 0, 1024))>0){
				out.write(buffer, 0, offset);
			}
			out.flush();
			requestInputStream.close();
		}
	}

	/**
	 * 填充頭部信息
	 */
	private void fillHeader() throws MalformedURLException {
		connection.addRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1653.0 Safari/537.36");
        connection.addRequestProperty("Content-type",contentType.contentType);
        if(header!=null){
            for (Map.Entry<String, String> entry : header.entrySet()) {
                connection.addRequestProperty(entry.getKey(), entry.getValue());
            }
		}
	}

	/**
	 * 將KEY/VALUE的形式參數進行拼接成字符串參數
	 * @param params
	 * @return
	 */
	private String parseParamsMap(Map<String, String> params) throws UnsupportedEncodingException {
        if(params.size()<=0){
            return null;
        }
		StringBuffer paramsStr = new StringBuffer();
		for (Map.Entry<String, String> entry : params.entrySet()) {
			paramsStr.append(entry.getKey()).append("=")
					.append(URLEncoder.encode(entry.getValue(), "utf-8")).append("&");
		}
		paramsStr.setLength(paramsStr.length() - 1);
		return paramsStr.toString();
	}

	/**
	 * 創建請求的響應實體
	 * @return
	 * @throws IOException
	 */
	private Response generateResponse() throws IOException {
        InputStream is=null;
        try{
            is = connection.getInputStream();
            Utils.COMMONS_LOGGER.debug("success handle remote server response");
            Response response= new Response(is,connection.getContentType());
            Header respHeader = new Header(connection.getHeaderFields());
            CookieStore cookieStore = manager.getCookieStore();
            List<HttpCookie> cookies = cookieStore.getCookies();
            for(HttpCookie cookie:cookies){
                respHeader.addCookie(cookie);
            }
            response.setHeader(respHeader);
            Utils.COMMONS_LOGGER.debug("response code:"+connection.getResponseCode());
            response.setResponseCode(connection.getResponseCode()+"");
            Utils.COMMONS_LOGGER.debug("response:"+response.toString());
            return response;
        }catch(IOException e){
            Utils.COMMONS_LOGGER.debug("request remote server occur exception",e);
            Header respHeader = new Header(connection.getHeaderFields());
            Response response = new Response(connection.getResponseCode()+"",connection.getResponseMessage(),respHeader);
            return response;
        }finally{
            if(out!=null) {
                out.close();
            }
            connection.disconnect();
            Utils.COMMONS_LOGGER.debug("disconnect remote server");
            if(is!=null){
                is.close();
            }
        }
	}

    enum ContentType{
        DEFAULT("www","application/x-www-form-urlencoded"),TEXT("text","text/plain;charset=UTF-8"),JSON("json","application/json;charset=UTF-8"),HTML("html","text/html;charset=UTF-8"),XML("xml","application/xml;charset=UTF-8");
        private String key;

        private String contentType;

        ContentType(String key,String contentType){
            this.key = key;
            this.contentType = contentType;
        }

        public static ContentType getType(String typeKey){
            ContentType[] contentTypes = ContentType.class.getEnumConstants();
            for(ContentType type:contentTypes){
                if(type.key.equalsIgnoreCase(typeKey)){
                    return type;
                }
            }
            return DEFAULT;
        }
    }
    private static class TrustAnyTrustManager implements X509TrustManager {

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[] {};
        }
    }
    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
}
